/*
 * Fichier initial :
 * esda/core/Timestamp.cxx --
 *
 * Copyright (C) Olivier Le Floch
 *
 * See the file LICENSE for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 */

#include "Timestamp.h"

namespace esda
{
	Timestamp::inttype Timestamp::undef =        INT64_MIN;
	Timestamp::inttype Timestamp::min   = -2147483648000LL;
	Timestamp::inttype Timestamp::max   =  2147483647000LL;

	Timestamp::Timestamp(inttype ms) : milliseconds(ms)
	{
		if ((ms < Timestamp::min || ms > Timestamp::max) && ms != Timestamp::undef) {
			ERROR("Timestamp value out of range");
		}
	}

	Timestamp &Timestamp::operator=(const Timestamp::inttype t)
	{
		if ((t >= Timestamp::min && t <= Timestamp::max) || t == Timestamp::undef) {
			milliseconds = t;
		} else {
			ERROR("Timestamp value out of range");
		}
		
		return *this;
	}

	Timestamp::inttype Timestamp::now(void)
	{
		struct timeval stamp;
	
		gettimeofday(&stamp, 0);
	
		return ((inttype) stamp.tv_sec) * ((inttype) 1000)
			+ ((inttype) stamp.tv_usec) / ((inttype) 1000);
	}

	Timestamp::inttype Timestamp::string2int(string s)
	{
		// TODO : Add error handling to this function (out of range Timestamps)
		
		bool isPureInt = true;
		inttype ms		 = 0;
	
		for (unsigned int i = 0; i < s.size(); ++i) {
			if (s[i] < '0' || s[i] > '9') {
				isPureInt = false;
				break;
			}
		
			ms = 10 * ms + (s[i] - '0');
		}
	
		if (isPureInt) {
			return ms;
		}
	
		int msec = 0;
		struct tm aTm;
	
		bzero(&aTm, sizeof(aTm));
	
		sscanf(s.c_str(), "%4d-%2d-%2dT%2d:%2d:%2d.%3dZ",
						&aTm.tm_year, &aTm.tm_mon, &aTm.tm_mday,
						&aTm.tm_hour, &aTm.tm_min, &aTm.tm_sec,
						&msec);
	
		aTm.tm_year -= 1900;
		aTm.tm_mon--;
	
		ms = ((inttype) timegm(&aTm)) * ((inttype) 1000L) + msec;
	
		return ms;
	}
	
	string Timestamp::int2string(Timestamp::inttype milliseconds)
	{
		time_t sec	 = (time_t) (milliseconds / 1000);
		inttype msec = milliseconds - (milliseconds / 1000) * 1000;
	
		if (milliseconds < 0 && msec != 0) {
			 sec--;
			 msec += 1000;
		}
	
		struct tm *tm = gmtime(&sec);
	
		stringstream result;
		result << setfill('0') << setw(4) << 1900+tm->tm_year
			<< "-" << setfill('0') << setw(2) << 1+tm->tm_mon
			<< "-" << setfill('0') << setw(2) << tm->tm_mday
			<< "T" << setfill('0') << setw(2) << tm->tm_hour 
			<< ":" << setfill('0') << setw(2) << tm->tm_min 
			<< ":" << setfill('0') << setw(2) << tm->tm_sec
			<< "." << setfill('0') << setw(3) << (int)msec 
			<< "Z";
	
		return result.str();
	}

	Timestamp::inttype Timestamp::ext2int(int year, int month, int day, int hour, int mini, int sec, int msec)
	{
		struct tm tm;
		tm.tm_sec	 = sec;
		tm.tm_min	 = mini;
		tm.tm_hour	= hour;
		tm.tm_mday	= day;
		tm.tm_mon	 = month-1;
		tm.tm_year	= year - 1900;
		tm.tm_wday	= tm.tm_yday = 0; // ignored anyway
		tm.tm_isdst = tm.tm_gmtoff = 0; // ignored anyway
		tm.tm_zone	= 0;
	
		return ((inttype) timegm(&tm)) * ((inttype) 1000L) + msec;
	}

	void Timestamp::int2ext(Timestamp::inttype t, int *year, int *month, int *day, int *hour, int *mini, int *sec, int *msec, int *wday, int *yday, bool gmt)
	{
		time_t seconds = (time_t) (t / 1000);
	
		if (msec) {
			*msec = (int) (t - seconds * 1000);
		}

		struct tm *tm = gmt ? gmtime(&seconds) : localtime(&seconds);
	
		if (year)	*year	= 1900 + tm->tm_year;
		if (month) *month = 1 + tm->tm_mon;
		if (day)	 *day	 = tm->tm_mday;
		if (hour)	*hour	= tm->tm_hour;
		if (mini)	 *mini	 = tm->tm_min;
		if (sec)	 *sec	 = tm->tm_sec;
		if (wday)	*wday	= tm->tm_wday;
		if (yday)	*yday	= tm->tm_yday;
	}

	Timestamp::inttype Timestamp::getLocalUTCOffset(void)
	{
		time_t seconds = (time_t) (now() / 1000);
		struct tm *tm	= localtime(&seconds);
	
		return tm->tm_gmtoff * 1000L;
	}
}
